import java.util.*;
import java.io.*;

public class Model 
{

    private ArrayList<Node> nodes;
    private Exploit ex;
    private final int max_depth;
    private Random rand;
    private boolean complete = false;
    private boolean success = false;

    public Model(String node_config, String network_config, Exploit ex, int max_depth)
    {
        this.rand = new Random();
        this.max_depth = max_depth;
        this.nodes = new ArrayList<Node>();
        this.ex = ex;
        try
        {
            BufferedReader in = new BufferedReader(new FileReader(node_config));
            String str = in.readLine();
            while (str != null)
            {
                this.parseNode(str);
                str = in.readLine();
            }
            in.close();

            in = new BufferedReader(new FileReader(network_config));
            str = in.readLine();
            while (str != null)
            {
                this.parseNeighbors(str);
                str = in.readLine();
            }
        }
        catch(FileNotFoundException e)
        {
            System.err.println("The Specified File Was Not Found!!");
            System.exit(-1);
        }
        catch(IOException e)
        {
            System.err.println("Error While Reading Config File");
            System.exit(-1);
        }
    }

    private void parseNode(String str)
    {
        if(str.equals(" ") || str == null)
        {
          System.err.println("1Nodes Config File in Wrong Format?");
          System.exit(-1);
        }

        String[] node = str.split(" ");

        if (node.length != 4)
        {
            System.err.println("2Nodes Config File in Wrong Format?");
            System.exit(-1);
        }

        try
        {
            int id = Integer.parseInt(node[0]);
            String name = node[1];
            String OS = node[2];
            boolean highvalue = node[3].toUpperCase().equals("HIGH");
            nodes.add(new Node(id, name, OS, highvalue));
        }
        catch (Exception e)
        {
            System.err.println("Error Parsing Nodes Config File");
            e.printStackTrace();
            System.exit(-1);
        }

    }

    public Node getRandomNode()
    {
        return nodes.get(rand.nextInt(nodes.size()));
    }

    public boolean runModel(Node n, int depth)
    {
        if (!nodes.contains(n))
        {
            System.err.println("Ran Model With Non-Existent Node");
            System.exit(-1);
        }
        
        if (depth <= this.max_depth && !n.isNeighborsAllCompromised())
        {
            depth++;
            if (this.pwned(n))
            {
                System.out.println("took node " + n.getID() + ": " + n.getName());
                if (n.isHighValue())
                {
                    System.out.println("High Value Target Owned, Exploit Succeeded");
                    return true;
                }
                return this.runModel(n.getRandomNeighbor(), depth++);
            }
            else
            {
                System.out.println("Exploit Failed at node " + n.getID() + ": " + n.getName());
                return false;
            }
        }
        else
        {
            if (depth > this.max_depth)
            {
                System.out.println("Model Complete: Max Depth reached");
                return false;
            }
            else if (n.isNeighborsAllCompromised())
            {
                System.out.println("Model Complete: No Where Else To Go");
                return false;
            }
            else
            {
                System.err.println("Something Else Happenend (NOT GOOD!!)");
                System.exit(-1);
            }

        }
        System.err.println("Something Really Bad Happened");    //Unreachable
        System.exit(-1);
        return false;
    }

    public boolean pwned(Node n)
    {
        Object hm_ret = ex.hashMap.get(n.getOS());
        if (hm_ret != null)
        {
            Double strength = (Double)ex.hashMap.get(n.getOS());
            if (strength > rand.nextDouble())
            {
                return true;
            }
        }
        return false;
    }

    private void parseNeighbors(String str)
    {
        String[] _node = str.split(":");
        if (_node.length == 1)
        {
            return;
        }
        String[] neighbors = _node[1].split(",");
        try 
        {
            Node node = this.findNodeByID(Integer.parseInt(_node[0]));
            if (node != null)
                for (int i = 0; i < neighbors.length; i++)
                {
                    Node neighbor = this.findNodeByID(Integer.parseInt(neighbors[i]));
                    if (neighbor == null)
                        break;
                    node.addNeighbor(neighbor);
                }
            else
            {
                System.err.append("Error Parsing Network Config File");
                System.exit(-1);
            }
        }
        catch (NumberFormatException e)
        {
            System.err.println("Error Parsing Network Config File");
            System.exit(-1);
        }
    }
    
    private Node findNodeByID(int id)
    {
        for(int i = 0; i < nodes.size(); i++)
        {
            if (nodes.get(i).getID() == id)
                return nodes.get(i);
        }
        return null;
    }

    public void resetModel()
    {
        for (int i = 0; i < nodes.size(); i++)
        {
            nodes.get(i).reset();
        }
    }

}